//
//  BPFMDBHelper.m
//  BP
//
//  Created by 404 on 2020/1/8.
//  Copyright © 2020年 404. All rights reserved.
//

#import "BPFMDBHelper.h"
#import <sqlite3.h>

static FMDatabaseQueue *dbQueue;
static NSString *currentDBPath = @"";
static NSRecursiveLock *GetSafeLock() {
    static NSRecursiveLock *tempSafeLock;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        tempSafeLock = [NSRecursiveLock new];
    });
    return tempSafeLock;
}

#define FMDBSaftLock GetSafeLock()

@implementation BPFMDBHelper
+ (BOOL)openDB:(NSString *)path{
    
    if ([currentDBPath isEqualToString:path] && dbQueue != nil) {
        return YES;
    }
    currentDBPath = path;
    [self closeDB];
    [FMDBSaftLock lock];
    if (path.length > 0) {
        dbQueue = [FMDatabaseQueue databaseQueueWithPath:path flags:SQLITE_OPEN_FULLMUTEX | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE];
    }
    if (dbQueue) {
        [self configVersion];
    }
    [FMDBSaftLock unlock];
    return (dbQueue != nil);
}

+ (void)closeDB{
    [FMDBSaftLock lock];
    if (dbQueue) {
        [dbQueue close];
    }
    dbQueue = nil;
    currentDBPath = @"";
    [FMDBSaftLock unlock];
}

+ (BOOL)isDBOpened{
    return (dbQueue != nil);
}

+ (BOOL)executeUpdate:(NSString *)sql withArguments:(NSArray *)arguments{
    [FMDBSaftLock lock];
    __block BOOL result = NO;
    [dbQueue inDatabase:^(FMDatabase * _Nonnull db) {
        result = [db executeUpdate:sql withArgumentsInArray:arguments];
    }];
    [FMDBSaftLock unlock];
    return result;
}

+ (void)executeQuery:(NSString *)sql withArguments:(NSArray *)arguments syncResult:(void (^)(FMResultSet * _Nonnull))syncResultBlock{
    [FMDBSaftLock lock];
    [dbQueue inDatabase:^(FMDatabase * _Nonnull db) {
        FMResultSet *resultSet = [db executeQuery:sql withArgumentsInArray:arguments];
        syncResultBlock(resultSet);
        [resultSet close];
    }];
    [FMDBSaftLock unlock];
}

+ (void)executeTransaction:(void (^)(FMDatabase * _Nonnull, BOOL * _Nonnull))transactionBlock{
    [FMDBSaftLock lock];
    [dbQueue inTransaction:^(FMDatabase * _Nonnull db, BOOL * _Nonnull rollback) {
        if (transactionBlock) {
            transactionBlock(db,rollback);
        }
    }];
    [FMDBSaftLock unlock];
}

+ (int)versionOfTable:(NSString *)table{
    [FMDBSaftLock lock];
    __block int version = 0;
    [dbQueue inDatabase:^(FMDatabase * _Nonnull db) {
        FMResultSet *rs = [db executeQuery:@"SELECT version FROM table_version WHERE table_name = ?", table];
        if ([rs next]) {
            version = [rs intForColumn:@"version"];
        }
        [rs close];
    }];
    [FMDBSaftLock unlock];
    return version;
}

+(BOOL)updateTable:(NSString *)table version:(int)version transaction:(BOOL (^)(FMDatabase * _Nonnull))updateTransaction{
    [FMDBSaftLock lock];
    __block BOOL result = NO;
    [self executeTransaction:^(FMDatabase * _Nonnull db, BOOL * _Nonnull rollback) {
        result = updateTransaction(db);
        if (result) {
            result = [db executeUpdate:@"REPLACE INTO table_version (table_name, version) VALUES (?, ?)", table, @(version)];
        }
        *rollback = !result;
    }];
    [FMDBSaftLock unlock];
    return result;
}

+ (BOOL)configVersion{
    NSString *sql = @"CREATE TABLE IF NOT EXISTS table_version ("
    "id INTEGER PRIMARY KEY AUTOINCREMENT,"
    "table_name TEXT NOT NULL UNIQUE,"   //表名
    "version INTEGER CHECK(version > 0)" //版本，必须大于0
    ")";
    return [self executeUpdate:sql withArguments:nil];
}

+ (BOOL)dropTable:(NSString *)table{
    NSString *sql = [NSString stringWithFormat:@"DROP TABLE IF EXISTS %@", table];
    return [self executeUpdate:sql withArguments:nil];
}

+ (BOOL)isExistTableWithName:(NSString *)tableName{
    [FMDBSaftLock lock];
    __block BOOL exists = NO;
    [dbQueue inDatabase:^(FMDatabase * _Nonnull db) {
        exists = [db tableExists:tableName];
    }];
    [FMDBSaftLock unlock];
    return exists;
}

+ (BOOL)isColumnExist:(NSString *)columnName inTable:(NSString *)tableName{
    [FMDBSaftLock lock];
    __block BOOL isExist = NO;
    [dbQueue inDatabase:^(FMDatabase *db) {
        isExist = [db columnExists:columnName inTableWithName:tableName];
    }];
    [FMDBSaftLock unlock];
    return isExist;
}
@end
